package com.junzhiit.nengji.shop.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.bainaeco.framework.enums.CommonStatus;
import com.bainaeco.framework.enums.IsDelete;
import com.bainaeco.framework.util.DBKey;
import com.bainaeco.framework.util.DateUtil;
import com.bainaeco.framework.util.JsonUtil;
import com.junzhiit.nengji.model.AuditLog;
import com.junzhiit.nengji.shop.web.util.Constants;
import com.junzhiit.nengji.shop.web.util.RequestUtil;
import com.junzhiit.nengji.shop.web.util.URLAnalysis;

@Aspect
@Component
public class WebLogAspect {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private List<String> excludeUris;

    @PostConstruct
    public void init() {
        logger.info("init");
        excludeUris = new ArrayList<String>();
        excludeUris.add(Constants.PAGE_NOT_FOUND_PATH);
        excludeUris.add(Constants.ERROR_PATH);
    }

    @PreDestroy
    public void destroy() {
        logger.info("destroy");
    }

    @Pointcut("execution(public * com.junzhiit.nengji.shop.web.controller..*.*(..)) && !execution(* com.junzhiit.nengji.shop.web.controller..*.initBinder(..))")
    public void webLog() {
    	
    }

    @Before("webLog()")
    public void doBefore(final JoinPoint joinPoint) throws Throwable {
        MDC.put("reqid", DBKey.getUUID());
        MDC.put("reqtime", String.valueOf(DateUtil.getCurrentDate().getTime()));

        logger.info("REQUEST START");
        // 接收到请求，记录请求内容
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            logger.info(joinPoint.toString());
            final HttpServletRequest request = attributes.getRequest();
            // // 记录下请求内容
            logger.info(request.toString());
            logger.info("URL : " + request.getRequestURI());
            logger.info("HTTP_METHOD : " + request.getMethod());
            logger.info("IP : " + RequestUtil.getRealIp(request));
            logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
        }
    }

    
    private AuditLog requestAuditLog(final JoinPoint joinPoint, final HttpServletRequest request) {
        final AuditLog auditLog = new AuditLog();
        auditLog.setCreatedTime(DateUtil.getCurrentDate());
        auditLog.setIsDelete(IsDelete.NO.toIntValue());

        final URLAnalysis urlAnalysis = (URLAnalysis) request.getAttribute(Constants.URL_ANALYSIS);
        if (urlAnalysis != null) {
            final String userId = urlAnalysis.getParam(Constants.USERID);
            if (StringUtils.isNotBlank(userId)) {
                MDC.put("uid", userId);
                try {
                    auditLog.setUserId(userId);
                } catch (final NumberFormatException e) {
                    logger.error(e.getMessage(), e);
                }
            }

            final String accessToken = urlAnalysis.getParam(Constants.ACCESSTOKEN);
            if (StringUtils.isNotBlank(accessToken)) {
                auditLog.setAccessToken(accessToken);
            }
        }

        auditLog.setUrl(request.getRequestURI());
        auditLog.setMethod(request.getMethod());
        auditLog.setIp(RequestUtil.getRealIp(request));
        auditLog.setParams(Arrays.toString(joinPoint.getArgs()));
        auditLog.setUserAgent(request.getHeader("User-Agent"));

        auditLog.setRequestTimestamp(MDC.get("reqtime"));
        auditLog.setResponseTimestamp(String.valueOf(DateUtil.getCurrentDate().getTime()));

        auditLog.setSid(request.getHeader(Constants.SESSIONID));

        return auditLog;
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(final JoinPoint joinPoint, final Object ret) throws Throwable {
        // 接收到请求，记录请求内容
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            final HttpServletRequest request = attributes.getRequest();
            // 处理完请求，返回内容
            final AuditLog auditLog = requestAuditLog(joinPoint, request);
            auditLog.setStatus(CommonStatus.SUCCESS.toValue());
            auditLog.setReqId(MDC.get("reqid"));
            if (ret != null) {
                String response = JsonUtil.toJson(ret);
                if (response.length() > 3000) {
                    response = response.substring(0, 3000);
                }
                auditLog.setResponse(response);
            }
            if (!excludeUris.contains(request.getRequestURI())) {
                logger.info(auditLog.toString());
            }

            // logger.debug("RESPONSE SUCCESS: " + ret);
            logger.info("RESPONSE SUCCESS");
        }

        MDC.remove("uid");
        MDC.remove("reqid");
    }

    @AfterThrowing(throwing = "exception", pointcut = "webLog()")
    public void doAfterThrowing(final JoinPoint joinPoint, final Throwable exception) throws Throwable {
        // 接收到请求，记录请求内容
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            final HttpServletRequest request = attributes.getRequest();
            // 处理完请求，返回内容
            final AuditLog auditLog = requestAuditLog(joinPoint, request);
            auditLog.setStatus(CommonStatus.FAILED.toValue());
            auditLog.setReqId(MDC.get("reqid"));
            auditLog.setResponse(exception.toString());
            if (!excludeUris.contains(request.getRequestURI())) {
                logger.info(auditLog.toString());
            }

            logger.error("RESPONSE FAILED: " + exception.toString());
        }

        MDC.remove("uid");
        MDC.remove("reqid");
    }
}